<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SmileyFace</title>
</head>
<body>
  <div id="container"></div>
</body>
<style>
  body {
    margin: 0;
    overflow: hidden;
  }
  #container {
    width: 100vw;
    height: 100vh;
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_ES
precision mediump float;
#endif

#define S(a, b, t) smoothstep(a, b, t)
#define sat(x) clamp(x, 0.0, 1.0)

float remap01(float a, float b, float t) {
  return sat((t - a) / (b - a));
}

float remap(float a, float b, float c, float d, float t) {
  return ((t - a) / (b - a)) * (d - c) + c;
}

vec2 within(vec2 uv, vec4 rect) {
  return (uv - rect.xy) / (rect.zw - rect.xy);
}

uniform vec2 u_resolution;

vec4 Head(vec2 uv) {
  vec4 col = vec4(.9, .65, .1, .1);
  float d = length(uv);
  col.a = S(.5, .49, d);
  float edgeShade = remap01(.35, .5, d);
  edgeShade *= edgeShade;
  col.rgb *= 1. - edgeShade * .5;

  col.rgb = mix(col.rgb, vec3(.6, .3, .1), S(.47, .48, d));

  float highlight = S(.41, .405, d);
  highlight *= remap(.41, .0, .75, .0, uv.y);
  col.rgb = mix(col.rgb, vec3(1.), highlight);

  d = length(uv - vec2(.25, -.2));
  float cheek = S(.2, .01, d) * .4;
  col.rgb = mix(col.rgb, vec3(1., .1, .1), cheek);
  return col;
}

vec4 Eye(vec2 uv) {
  // center
  uv -= .5;
  vec4 col = vec4(1.);
  
  float d = length(uv);

  // outline
  col.rgb *= 1. - S(.45, .5, d) * .5 * sat(-uv.y - uv.x);

  // background
  vec4 irisCol = vec4(.3, .5, 1., 1.);
  col = mix(col, irisCol, S(.1, .7, d) * .5);

  // eye ball
  col.rgb = mix(col.rgb, vec3(0.), S(.3, .28, d));
  irisCol.rgb *= 1. + S(.3, .05, d);
  col.rgb = mix(col.rgb, irisCol.rgb, S(.28, .25, d));

  col.rgb = mix(col.rgb, vec3(0.), S(.15, .14, d));

  // reflection
  float highlight = S(.1, .09, length(uv - vec2(-.15, .15)));
  highlight += S(.07, .05, length(uv + vec2(-.08, .08)));
  col.rgb = mix(col.rgb, vec3(1.), highlight);

  col.a = S(.5, .48, d);
  return col;
}

vec4 Mouth(vec2 uv) {
  uv -= .5;
  vec4 col = vec4(.5, .18, .05, 1.);

  uv.y *= 1.5;
  uv.y -= uv.x * uv.x * 2.;

  float d = length(uv);
  col.a = S(.5, .48, d);

  // teeth
  float td = length(uv - vec2(0., .6));
  vec3 toothCol = vec3(1.) * S(.6, .35, d);
  col.rgb = mix(col.rgb, toothCol, S(.4, .37, td));

  // tongue
  td = length(uv + vec2(0., .5));
  col.rgb = mix(col.rgb, vec3(1., .5, .5), S(.5, .2, td));
  return col;
}

vec4 Smiley(vec2 uv) {
  vec4 col = vec4(0.);
  uv.x = abs(uv.x);

  vec4 head = Head(uv);
  vec4 eye = Eye(within(uv, vec4(.03, -.1, .37, .25)));
  vec4 mouth = Mouth(within(uv, vec4(-.3, -.4, .3, -.1)));

  col = mix(col, head, head.a);
  col = mix(col, eye, eye.a);
  col = mix(col, mouth, mouth.a);
  return col;
}

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.xy;
    uv -= .5;
    uv.x *= u_resolution.x/u_resolution.y;
    gl_FragColor=Smiley(uv);
}
</script>
<script>
        var container;
        var camera, scene, renderer, clock;
        var uniforms;

        init();
        animate();

        function init() {
            container = document.getElementById( 'container' );

            camera = new THREE.Camera();
            camera.position.z = 1;

            scene = new THREE.Scene();
            clock = new THREE.Clock();

            var geometry = new THREE.PlaneBufferGeometry( 2, 2 );

            uniforms = {
                u_time: { type: "f", value: 1.0 },
                u_resolution: { type: "v2", value: new THREE.Vector2() },
                u_mouse: { type: "v2", value: new THREE.Vector2() }
            };

            var material = new THREE.ShaderMaterial( {
                uniforms: uniforms,
                fragmentShader: document.getElementById( 'fragmentShader' ).textContent
            } );

            var mesh = new THREE.Mesh( geometry, material );
            scene.add( mesh );

            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );

            container.appendChild( renderer.domElement );

            onWindowResize();
            window.addEventListener( 'resize', onWindowResize, false );

            document.onmousemove = function(e){
              uniforms.u_mouse.value.x = e.pageX
              uniforms.u_mouse.value.y = e.pageY
            }
        }

        function onWindowResize( event ) {
            renderer.setSize( window.innerWidth, window.innerHeight );
            uniforms.u_resolution.value.x = renderer.domElement.width;
            uniforms.u_resolution.value.y = renderer.domElement.height;
        }

        function animate() {
            requestAnimationFrame( animate );
            render();
        }

        function render() {
            uniforms.u_time.value += clock.getDelta();
            renderer.render( scene, camera );
        }
</script>
</html>